
package astralmlprocessor;

import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author age
 */
public class AstralMLCreator {
    
    // Objeto singleton
    public static AstralMLCreator instance;

    public static AstralMLCreator getInstance () { 
        if (instance == null) {
            newInstance ();
        }
        return instance;
    }
    
    public static void newInstance () { 
        AstralMLCreator.instance = new AstralMLCreator(); 
    }
      
    //XML tags constants
    public static final class Tags {
        public static final String 
                PRO = "Proyecto", 
                NOT = "notes",
                MET = "Meta_Object", 
                OBJ = "object.name",
                COO = "coordinates",
                EPH = "ephem",
                STA = "star.type",
                ECL = "eclipse",
                FIL = "Series.Filter", 
                DAT = "Serie.Date", 
                OBS = "serie.observer", 
                PUN = "m";
    }
    
    // XML tags attributes constants
    public static final class TagsAttr {
        public static final String 
                PRO_JD = "JD.prefix",
                PRO_ID = "project.ID",
                PRO_OBJ = "object.ID",
                PRO_DCR = "date.created",
                PRO_DMO = "date.modified",
                PRO_CRE = "creator",
                OBJ_NAM = "name",
                //OBJ_CAT = "cat",
                //OBJ_NUM = "num",
                COO_AR = "ar",
                COO_DEC = "dec",
                EPH_P = "p",
                EPH_EPO = "epoc",
                STA_ESP = "espec",
                STA_VAR = "vartype",
                ECL_A1 = "a1",
                ECL_A2 = "a2",
                ECL_D1 = "d1",
                ECL_D2 = "d2",
                ECL_MAX = "max",
                ECL_MIN = "min",
                FIL_TIP = "filter", 
                DAT_DAT = "date.jd", 
                OBS_OBS = "observer", 
                OBS_EXP = "t.exp",
                PUN_X = "jd",
                PUN_Y = "mg",
                PUN_ER = "er";
    }

    // Attribute values
    public static final class Fil_Tip_Values  {
        public static final String 
                B = "B", V = "V", R = "R", I = "I";
        
        public static String[] getValues() {
            Field [] fields = Fil_Tip_Values.class.getFields();
            String [] values = new String [fields.length];
            for (int i=0; i<fields.length; i++)
                values[i] = fields[i].getName();
            return values;
        }
    }
    public static final class Obs_Id_Values  {
        public static final String 
                PPS = "PPS", FSA = "FSA", AGE = "AGE";

        public static String[] getValues() {
            Field [] fields = Obs_Id_Values.class.getFields();
            String [] values = new String [fields.length];
            for (int i=0; i<fields.length; i++)
                values[i] = fields[i].getName();
            return values;
        }
    }

    public static String dateFormat(Date d) {
        return d.toString();  
    }

    public static String xmlFileName = "astronomy"; // Default file name
    public static String prefix = "2453000";
    
    // Document attributes
    private ProyectoInfo pi;
    private Document document;
    private Element raiz;
    private Element elProNot;
    private Element elMet;
    private Element elMetNot;
    private Element elMetCoo;
    private Element elMetEph;
    private Element elMetSta;
    private Element elMetEcl;

    public ProyectoInfo getPi() {
        return pi;
    }
    
    public AstralMLCreator() {
        commonConstructor(null);
        for (int i=0; i<Obs_Id_Values.getValues().length; i++)
            System.out.println(Obs_Id_Values.getValues()[i]);
    }

    public AstralMLCreator(String fileName) {
        commonConstructor(fileName);
    }
    
    private void commonConstructor(String fileName) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            DOMImplementation implementation = builder.getDOMImplementation();
            document = implementation.createDocument(null, Tags.PRO, null);
            raiz = document.getDocumentElement();
            
            document.setXmlVersion("1.0");
            
            if (fileName != null && !fileName.isEmpty()) {
                xmlFileName = fileName;
            }
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(AstralMLCreator.class.getName()).log(Level.SEVERE, null, ex);
        }
        pi = new ProyectoInfo();
    }
    
    public void modifBasicStructure(ProyectoInfo basedPi) {
        pi = basedPi;
        
        raiz.setAttribute(TagsAttr.PRO_JD, pi.jd);
        raiz.setAttribute(TagsAttr.PRO_ID, pi.id);
        raiz.setAttribute(TagsAttr.PRO_OBJ, pi.obj);
        raiz.setAttribute(TagsAttr.PRO_DCR, pi.dcr);
        raiz.setAttribute(TagsAttr.PRO_DMO, pi.dmo);
        raiz.setAttribute(TagsAttr.PRO_CRE, pi.cre);
        
        elProNot.setTextContent(pi.proNot);

        elMetNot.setTextContent(pi.metNot);
        NodeList listObj = elMet.getElementsByTagName(Tags.OBJ);
        while (listObj.getLength() > 0) {  
            Element el = (Element) listObj.item(0);
            elMet.removeChild(el);
        }
        for (int i=0; i<pi.metObj.length; i++) {
            Element elMetObj = document.createElement(Tags.OBJ);
            elMetObj.setAttribute(TagsAttr.OBJ_NAM, pi.metObj[i]);
            elMet.appendChild(elMetObj);
        }

        elMetCoo.setAttribute(TagsAttr.COO_AR, pi.metCooAr);
        elMetCoo.setAttribute(TagsAttr.COO_DEC, pi.metCooDec);

        elMetEph.setAttribute(TagsAttr.EPH_P, pi.metEphP);
        elMetEph.setAttribute(TagsAttr.EPH_EPO, pi.metEphEpo);
        
        elMetSta.setAttribute(TagsAttr.STA_ESP, pi.metStaEsp);
        elMetSta.setAttribute(TagsAttr.STA_VAR, pi.metStaVar);

        elMetEcl.setAttribute(TagsAttr.ECL_A1, pi.metEclA1);
        elMetEcl.setAttribute(TagsAttr.ECL_A2, pi.metEclA2);
        elMetEcl.setAttribute(TagsAttr.ECL_D1, pi.metEclD1);
        elMetEcl.setAttribute(TagsAttr.ECL_D2, pi.metEclD2);
        elMetEcl.setAttribute(TagsAttr.ECL_MAX, pi.metEclMax);
        elMetEcl.setAttribute(TagsAttr.ECL_MIN, pi.metEclMin);
    }
    
    public void createBasicStructure(ProyectoInfo basedPi) {            
            pi = basedPi;

            // Crear elementos basicos
            elProNot = document.createElement(Tags.NOT);
            raiz.appendChild(elProNot);
            elMet = document.createElement(Tags.MET);
            raiz.appendChild(elMet);
          
            // Crear elementos meta (oblig y extras)
            elMetNot = document.createElement(Tags.NOT);
            elMet.appendChild(elMetNot);
            elMetCoo = document.createElement(Tags.COO);
            elMet.appendChild(elMetCoo);
            elMetEph = document.createElement(Tags.EPH);
            elMet.appendChild(elMetEph);
            elMetSta = document.createElement(Tags.STA);
            elMet.appendChild(elMetSta);
            elMetEcl = document.createElement(Tags.ECL);
            elMet.appendChild(elMetEcl);
            
            modifBasicStructure(pi);            
    }
    
    public String getString () {
        String str = "";
        
	Source domSource = new DOMSource(document);

	StringWriter writer = new StringWriter();
	Result result = new StreamResult(writer);

        try {
            Transformer xformer = TransformerFactory.newInstance().newTransformer();
            xformer.transform(domSource, result);
            str = writer.toString();
        } catch (Exception ex) {
            Logger.getLogger(AstralMLCreator.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return str;
    }
    
    public void print() {
            Source source = new DOMSource(document);
            Result console = new StreamResult(System.out);

        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.transform(source, console);
        } catch (Exception ex) {
            Logger.getLogger(AstralMLCreator.class.getName()).log(Level.SEVERE, null, ex);
        }        
    }

    public void save(String filename) {
        xmlFileName = filename;
        save();
    }

    public void save() {
            Source source = new DOMSource(document);
            Result result = new StreamResult(new File(xmlFileName+".xml")); 

        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.transform(source, result);
        } catch (Exception ex) {
            Logger.getLogger(AstralMLCreator.class.getName()).log(Level.SEVERE, null, ex);
        }        
    }

    public void savePath(String path) {
            Source source = new DOMSource(document);
            Result result = new StreamResult(new File(path+"/"+xmlFileName+".xml")); 

        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.transform(source, result);
        } catch (Exception ex) {
            Logger.getLogger(AstralMLCreator.class.getName()).log(Level.SEVERE, null, ex);
        }        
    }

    /*
    public void addSesion(String filTipVal, String obsIdVal, Date fecha, String texp, String notseries, String notobserver, ArrayList<PuntoAstro> puntos) {
        addSesion(filTipVal, obsIdVal, dateFormat(fecha), texp, notseries, notobserver, puntos);
    }
    */
    
    public String getVectorPuntosTXT (String separador) {
        return "11.11" + separador + "22.22" + separador + "33.33" + "\n" +
               "11.11" + separador + "22.22" + separador + "33.33";
    }
    
    public void addSesion(String filTipVal, String obsIdVal, String fecha, String texp, String notseries, String notobserver, ArrayList<PuntoAstro> puntos) {
        NodeList listFil = raiz.getElementsByTagName(Tags.FIL);
        Element filtro = (Element) XmlUtils.getItem(listFil, TagsAttr.FIL_TIP, filTipVal);
        if (filtro == null) {
            filtro = document.createElement(Tags.FIL);
            filtro.setAttribute(TagsAttr.FIL_TIP, filTipVal);
            filtro.appendChild(document.createElement(Tags.NOT));
            raiz.appendChild(filtro);
        }
        if (notseries == null) notseries = "";
        ((Element) filtro.getFirstChild()).setTextContent(notseries);
        
        NodeList listObs = filtro.getElementsByTagName(Tags.OBS);
        Element observador = (Element) XmlUtils.getItem(listObs, TagsAttr.OBS_OBS, obsIdVal);
        if (observador == null) {
            observador = document.createElement(Tags.OBS);
            observador.setAttribute(TagsAttr.OBS_OBS, obsIdVal);
            observador.appendChild(document.createElement(Tags.NOT));
            filtro.appendChild(observador);
        }
        if (notobserver == null) notobserver = "";
        ((Element) observador.getFirstChild()).setTextContent(notobserver);
        observador.setAttribute(TagsAttr.OBS_EXP, texp);
                
        Element sesion = document.createElement(Tags.DAT);
        sesion.setAttribute(TagsAttr.DAT_DAT, fecha);
        observador.appendChild(sesion);
         
        for (PuntoAstro p : puntos) {
            Element punto = document.createElement(Tags.PUN);
            punto.setAttribute(TagsAttr.PUN_X, p.getJd().toString());
            punto.setAttribute(TagsAttr.PUN_Y, p.getMg().toString());
            punto.setAttribute(TagsAttr.PUN_ER, p.getEr().toString());
            if (p.getComment() != null && !p.getComment().isEmpty())
                punto.setTextContent(p.getComment());
            sesion.appendChild(punto);
        }         
    }
    
    static public class ProyectoInfo {
        public String jd = "", id = "", obj = "", dcr = "",
            dmo = "", cre = "", proNot = "", metNot = "", 
            metCooAr = "", metCooDec = "", metEphP = "", metEphEpo = "",
            metStaEsp = "", metStaVar = "", metEclA1 = "", metEclA2 = "",
            metEclD1 = "", metEclD2 = "", metEclMax = "", metEclMin = "";
        public String [] metObj = null;

        public ProyectoInfo() {}
        public ProyectoInfo(String jd, String id, String obj, String dcr, 
                String dmo, String cre, String proNot, String metNot, 
                String metCooAr, String metCooDec, String metEphP, 
                String metEphEpo, String metStaEsp, String metStaVar, 
                String metEclA1, String metEclA2, String metEclD1, 
                String metEclD2, String metEclMax, String metEclMin, 
                String[] metObj) {
            this.jd = jd;
            this.id = id;
            this.obj = obj;
            this.dcr = dcr;
            this.dmo = dmo;
            this.cre = cre;
            this.proNot = proNot;
            this.metNot = metNot;
            this.metCooAr = metCooAr;
            this.metCooDec = metCooDec;
            this.metEphP = metEphP;
            this.metEphEpo = metEphEpo;
            this.metStaEsp = metStaEsp;
            this.metStaVar = metStaVar;
            this.metEclA1 = metEclA1;
            this.metEclA2 = metEclA2;
            this.metEclD1 = metEclD1;
            this.metEclD2 = metEclD2;
            this.metEclMax = metEclMax;
            this.metEclMin = metEclMin;
            this.metObj = metObj;
        }
    }
}
